// VCS_MODEL async native fifo
// ONLY DIFFERENCE IN RAM PARAM & NAME
// rxfifo 40g data
module async_fifo_d16384_w128 
    (
        // SYS
        input                 wclk               ,
        input                 wr_rstn            ,
        input                 rclk               ,
        input                 rd_rstn            ,
        input          [11:0] ram_dp_cfg_register,

        // WCLK
        input                 wr_en_i   ,
        input         [127:0] wr_din_i  , // Need to be buffered
        output wire           full_o    , // 1T after wr_en_i
        
        // RCLK
        input                 rd_en_i   ,
        output reg   [127:0]  rd_dout_o , // Need to be buffered
        output wire           empty_o     // 1T after rd_en_i
    );

`ifdef ORIGINAL_FIFOCTL
// CDC
// Next(Wire) > Currunt(Reg)
// Next(Wire) > Gray_Next(Wire) > Gray(Reg) > Sync(Reg) > Rx_Next(Wire) > Judge
reg  [14 : 0] wr_ptr_gray ;
reg  [14 : 0] rd_ptr_gray ;
//======================================================================================================
// WCLK Domain
wire          mem_wr_en         ;
wire [14 : 0] wr_ptr_next       ;
reg  [14 : 0] wr_ptr            ;
wire [14 : 0] wr_ptr_gray_next  ;
// RCLK CDC, Latency = 1 Src_Clk + 2 Des_Clk
reg  [14 : 0] rd_ptr_gray_sync0 ;
reg  [14 : 0] rd_ptr_gray_sync1 ;
wire [14 : 0] rd_ptr_rx_next    ;

assign mem_wr_en = wr_en_i & ( !full_o ) ; // Write Protection
assign wr_ptr_next = ( mem_wr_en ) ? ( wr_ptr + 'd1 ) : wr_ptr ;

// Next(Wire) > Currunt(Reg)
always @( posedge wclk or negedge wr_rstn ) begin
    if ( !wr_rstn ) begin
        wr_ptr <= 'd0 ;
    end
    else if ( mem_wr_en ) begin
        wr_ptr <= wr_ptr_next ;
    end
    else begin
        wr_ptr <= wr_ptr ;
    end
end

// Next(Wire) > Gray_Next(Wire)
genvar wr_tx;
generate
    for ( wr_tx = 0; wr_tx <= 14; wr_tx = wr_tx + 1 ) begin: gen_bin2gray_wr_tx
        if ( wr_tx == 14 )
        begin: wr_tx_gen0
            assign wr_ptr_gray_next[wr_tx] = wr_ptr_next[wr_tx] ;
        end
        else
        begin: wr_tx_gen1
            assign wr_ptr_gray_next[wr_tx] = wr_ptr_next[wr_tx] ^ wr_ptr_next[wr_tx+1] ;
        end
    end
endgenerate

// Gray_Next(Wire) > Gray(Reg)
always @( posedge wclk or negedge wr_rstn ) begin
    if ( !wr_rstn ) begin
        wr_ptr_gray <= 'd0 ;
    end
    else if ( mem_wr_en ) begin
        wr_ptr_gray <= wr_ptr_gray_next ;
    end
    else begin
        wr_ptr_gray <= wr_ptr_gray ;
    end
end

// Gray(Reg) > Sync(Reg)
always @( posedge wclk or negedge wr_rstn ) begin
    if ( !wr_rstn ) begin
        rd_ptr_gray_sync0 <= 1'b0 ;
        rd_ptr_gray_sync1 <= 1'b0 ;
    end
    else begin
        rd_ptr_gray_sync0 <= rd_ptr_gray ;
        rd_ptr_gray_sync1 <= rd_ptr_gray_sync0 ;
    end
end

// Sync(Reg) > Rx_Next(Wire)
genvar rd_rx;
generate
    for ( rd_rx = 0; rd_rx <= 14; rd_rx = rd_rx + 1 ) begin: gen_gray2bin_rd_rx
        if ( rd_rx == 14 )
        begin: rd_rx_gen0
            assign rd_ptr_rx_next[rd_rx] = rd_ptr_gray_sync1[rd_rx] ;
        end
        else
        begin: rd_rx_gen1
            assign rd_ptr_rx_next[rd_rx] = rd_ptr_gray_sync1[rd_rx] ^ rd_ptr_rx_next[rd_rx+1] ;
        end
    end
endgenerate

// Judge
always @( posedge wclk or negedge wr_rstn ) begin
    if ( !wr_rstn ) begin
        full_o <= 1'b1 ;
    end
    else if ( (wr_ptr_next[14] != rd_ptr_rx_next[14]) && (wr_ptr_next[14-1 : 0] == rd_ptr_rx_next[14-1 : 0]) ) begin
        full_o <= 1'b1 ;
    end
    else begin
        full_o <= 1'b0 ;
    end
end

//======================================================================================================
// RCLK Domain
wire          mem_rd_en         ;
wire [14 : 0] rd_ptr_next       ;
reg  [14 : 0] rd_ptr            ;
wire [14 : 0] rd_ptr_gray_next  ;
// RCLK CDC, Latency = 1 Src_Clk + 2 Des_Clk
reg  [14 : 0] wr_ptr_gray_sync0 ;
reg  [14 : 0] wr_ptr_gray_sync1 ;
wire [14 : 0] wr_ptr_rx_next    ;

assign mem_rd_en = rd_en_i & ( !empty_o ) ; // Read Protection
assign rd_ptr_next = ( mem_rd_en ) ? ( rd_ptr + 'd1 ) : rd_ptr ;

// Next(Wire) > Currunt(Reg)
always @( posedge rclk or negedge rd_rstn ) begin
    if ( !rd_rstn ) begin
        rd_ptr <= 'd0 ;
    end
    else if ( mem_rd_en ) begin
        rd_ptr <= rd_ptr_next ;
    end
    else begin
        rd_ptr <= rd_ptr ;
    end
end

// Next(Wire) > Gray_Next(Wire)
genvar rd_tx;
generate
    for ( rd_tx = 0; rd_tx <= 14; rd_tx = rd_tx + 1 ) begin: gen_bin2gray_rd_tx
        if ( rd_tx == 14 )
        begin: rd_tx_gen0
            assign rd_ptr_gray_next[rd_tx] = rd_ptr_next[rd_tx] ;
        end
        else
        begin: rd_tx_gen1
            assign rd_ptr_gray_next[rd_tx] = rd_ptr_next[rd_tx] ^ rd_ptr_next[rd_tx+1] ;
        end
    end
endgenerate

// Gray_Next(Wire) > Gray(Reg)
always @( posedge rclk or negedge rd_rstn ) begin
    if ( !rd_rstn ) begin
        rd_ptr_gray <= 'd0 ;
    end
    else if ( mem_rd_en ) begin
        rd_ptr_gray <= rd_ptr_gray_next ;
    end
    else begin
        rd_ptr_gray <= rd_ptr_gray ;
    end
end

// Gray(Reg) > Sync(Reg)
always @( posedge rclk or negedge rd_rstn ) begin
    if ( !rd_rstn ) begin
        wr_ptr_gray_sync0 <= 1'b0 ;
        wr_ptr_gray_sync1 <= 1'b0 ;
    end
    else begin
        wr_ptr_gray_sync0 <= wr_ptr_gray ;
        wr_ptr_gray_sync1 <= wr_ptr_gray_sync0 ;
    end
end

// Sync(Reg) > Rx_Next(Wire)
genvar wr_rx;
generate
    for ( wr_rx = 0; wr_rx <= 14; wr_rx = wr_rx + 1 ) begin: gen_gray2bin_wr_rx
        if ( wr_rx == 14 )
            assign wr_ptr_rx_next[wr_rx] = wr_ptr_gray_sync1[wr_rx] ;
        else
            assign wr_ptr_rx_next[wr_rx] = wr_ptr_gray_sync1[wr_rx] ^ wr_ptr_rx_next[wr_rx+1] ;
    end
endgenerate

// Judge
always @( posedge rclk or negedge rd_rstn ) begin
    if ( !rd_rstn ) begin
        empty_o <= 1'b1 ;
    end
    else if ( rd_ptr_next == wr_ptr_rx_next ) begin
        empty_o <= 1'b1 ;
    end
    else begin
        empty_o <= 1'b0 ;
    end
end

`else

wire  [13:0] fifo_wr_addr;
wire  [13:0] fifo_rd_addr;
wire         mem_wr_en;


DW_fifoctl_s2_sf #
(
    .depth(16384),
    .push_ae_lvl(2),
    .push_af_lvl(8),
    .pop_ae_lvl(2),
    .pop_af_lvl(2),
    .err_mode(0),
    .rst_mode(0),
    .push_sync(2),
    .pop_sync(2),
    .tst_mode(0)
)u_fifo_ctl
(
    .clk_push(wclk),
    .clk_pop(rclk),
    .rst_n(wr_rstn),
    .push_req_n(~wr_en_i), // push
    .pop_req_n(~rd_en_i), // pop
    .we_n(mem_wr_en), // wr_en to ram
    .push_empty(),
    .push_ae(),
    .push_hf(),
    .push_af(full_o), // wr_afull
    .push_full(), // wr_full
    .push_error(),
    .pop_empty(empty_o), // rd_empty
    .pop_ae(),
    .pop_hf(),
    .pop_af(),
    .pop_full(),
    .pop_error(),
    .wr_addr(fifo_wr_addr), // wr_addr to ram
    .rd_addr(fifo_rd_addr), // rd_addr to ram
    .push_word_count(),
    .pop_word_count(),
    .test(1'b0)
);


`endif

wire  [127:0] rd_dout;
reg           mem_rd_en_ff;

always @( posedge rclk or negedge rd_rstn ) 
begin
    if ( !rd_rstn ) 
        mem_rd_en_ff <= 'd0 ;
    else
        mem_rd_en_ff <= rd_en_i;
end

always @( posedge rclk or negedge rd_rstn ) 
begin
    if ( !rd_rstn ) 
        rd_dout_o <= 'd0 ;
    else if(mem_rd_en_ff)
        rd_dout_o <= rd_dout ;
    else
        rd_dout_o <= rd_dout_o ;
end

//======================================================================================================
// FIFO MEM
`ifdef FPGA
`else
    ram_dp_d16384_w128_wrapper U_ram_dp_d16384_w128_wrapper(
        .clka(wclk),
        .clkb(rclk),
        .ram_dp_cfg_register(ram_dp_cfg_register),

        .wea(~mem_wr_en),
        .web(1'b1),

        .addra(fifo_wr_addr),
        .addrb(fifo_rd_addr),

        .dina(wr_din_i),
        .dinb(128'd0),

        .douta(),
        .doutb(rd_dout)
    );
`endif

endmodule
